#include "space_object.hh"

#include <string.h>
#include "cuda_helper.h"
#include "space_object_kernel.cuh"

SpaceObject::SpaceObject(SpaceObjectAtlas *atlas){
	m_atlas = atlas;
	m_allocated = 0;
	m_capacity_grower = 10;
	m_count = 0;
	m_device = NULL;
	m_host = NULL;
	m_device_actual = false;
}

void SpaceObject::EnsureCapacity(unsigned int new_capacity){
	if(new_capacity > m_allocated){
		unsigned int new_allocated = ((new_capacity - 1) / m_capacity_grower + 1) * m_capacity_grower;
		SO_Data *new_data = (SO_Data *)malloc(sizeof(SO_Data) * new_allocated);
		memset(new_data, 0, sizeof(SO_Data) * new_allocated);
		memcpy(new_data, m_host, sizeof(SO_Data) * m_count);

		if(m_host != NULL) free(m_host);

		m_host = new_data;
		m_allocated = new_allocated;
	}
}

void SpaceObject::AddObject(float x, float y, float z, unsigned int id, unsigned int atlas_id){
	EnsureCapacity(m_count + 1);

	m_host[m_count].atlas_id = atlas_id;
	m_host[m_count].id = id;
	m_host[m_count].x = x;
	m_host[m_count].y = y;
	m_host[m_count++].z = z;

	m_device_actual = false;
}

void SpaceObject::MakeDeviceActual(){
	if(m_device_actual) return;

	if(m_device != NULL) CUDA_CALL(cudaFree(m_device));
	CUDA_CALL(cudaMalloc((void **)&m_device, sizeof(SO_Data) * m_count));
	CUDA_CALL(cudaMemcpy(m_device, m_host, sizeof(SO_Data) * m_count, cudaMemcpyHostToDevice));

	m_device_actual = true;
}

void SpaceObject::Prepare(){
	MakeDeviceActual();
#ifdef CUDA_VARIANT_GPU3
	space_object_cuda_prepare(m_device, m_atlas->getDevice(), m_count);
#endif
}
void SpaceObject::Collide(){

	space_object_cuda_collide(m_device, m_atlas->getDevice(), m_count);

	CUDA_CALL(cudaMemcpy(m_host, m_device, sizeof(SO_Data) * m_count, cudaMemcpyDeviceToHost));
}

SpaceObject::SO_Data *SpaceObject::getObjects(){
	return m_host;
}

